/*
 * CafeGo v1.0.0
 * http://dev.akinao.com/cafego/
 * Copyright (c) 2009 Akinao Saito
 */
(function(){

	var Lang = akinao.lang;
	var Go = akinao.game.go.Go;
	var Point = akinao.game.Point;
	var Sgf = akinao.game.Sgf;

	var INTERVAL_SPEED = {first: 1000, middle: 3000, slow: 5000};

	CafeGo.board = [];

	function CafeGo(id){
		var element = $("#" + id);
		var sgf = element.text();
		element.html("");
		this._sgf = parseSgf(sgf);
		this._go = new Go(this._sgf.info().HA);
		this._id = id;
		this._element = element;

		var size = 400;
		this._view = {
			board: size,
			stone: size / 20,
			padding: size / 40,
			numbering: true
		};

		this._infoWindow = createGameInfo(this);
		this._controlWindow = createControlWindow(this);

		this._play = {
			isPlay: false,
			interval: INTERVAL_SPEED.middle,
			timerID: null,
			animate: []
		};

		this._lockControl = false;

		CafeGo.board[this._id] = this;

		var turn = this._go.getAll();
		if (false == Lang.isUndefined(turn.values)) {
			updateTurn(this._id, turn.values);
		}
		updateDisplay(this._id)
	}

	function parseSgf(value) {
		try {
			return new Sgf(value);
		} catch (e) {
			return new Sgf("(;ERR[SGF is invalid])");
		}
	}


	CafeGo.get = function(id) {
		return CafeGo.board[id];
	};

	/*
	 *
	 */
	CafeGo.prototype.next = function() {
		playPause(this._id);
		if (false == this._sgf.next()) {
			return;
		}
		var node = parseTurn(this._sgf.get());
		if (node.error) {
			return;
		}
		var turn = this._go.set(node.player, node.point);
		if (turn.error) {
			return;
		}
		// update display
		updateTurnOne(this._id, turn.values);
		updateDisplay(this._id);
	};

	/*
	 *
	 */
	CafeGo.prototype.previous = function() {
		playPause(this._id);
		if (false == this._sgf.previous()) {
			return;
		}
		var turn = this._go.previous();
		if (turn.error) {
			return;
		}
		// update display
		updateTurnOne(this._id, turn.values);
		updateDisplay(this._id);
	};

	/*
	 *
	 */
	CafeGo.prototype.first = function() {
		playPause(this._id);
		this._go.clear();
		this._sgf.first();
		removeAllStone(this._id);
		updateDisplay(this._id);
	};

	/*
	 *
	 */
	CafeGo.prototype.last = function() {
		playPause(this._id);
		while (this._sgf.next()) {
			var node = parseTurn(this._sgf.get());
			if (node.error) {
				return;
			}
			var turn = this._go.set(node.player, node.point);
			if (turn.error) {
				break;
			}
			updateTurn(this._id, turn.values);
		}
		displayNowStone(this._id);
		updateDisplay(this._id);
	};

	/*
	 *
	 */
	CafeGo.prototype.play = function() {
		if (this._sgf.count == this._go.turnCount) {
			this.first();
		}
		if (this._play.isPlay) {
			playPause(this._id);
		} else {
			this._play.isPlay = true;
			playAuto(this._id);
			this._controlWindow.animate({top: this._view.board}, 500);
			this._infoWindow.animate({top: - this._infoWindow.outerHeight() + "px"}, 500);
		}
		updatePlayButton(this._id);
	};

	/*
	 *
	 */
	CafeGo.prototype.interval = function(value) {
		if (Lang.isNumber(value)) {
			this._play.interval = value;
		} else if (false == Lang.isUndefined(INTERVAL_SPEED[value])) {
			this._play.interval = INTERVAL_SPEED[value];
		} else {
			this._play.interval = INTERVAL_SPEED.midlle;
		}
	};

	/*
	 *
	 */
	CafeGo.prototype.numbering = function(display) {
		if (display && this._view.numbering == display) {
			return;
		}
		this._view.numbering = !this._view.numbering;
		displayNumber(this._id, this._view.numbering);
		var lastTurn = this._go.lastTurn();
		updateNumberButton(this._id);
	};

	/*
	 *
	 */
	CafeGo.prototype.openInfo = function(isOpen) {
		toggleInfoWindow({data:{id: this._id, open: isOpen}});
	};

	/*
	 *
	 */
	CafeGo.prototype.openControl = function(isOpen) {
		toggleControlWindow({data:{id: this._id, open: isOpen}});
	};

	/*
	 *
	 */
	CafeGo.prototype.sgf = function(sgf) {
		this._sgf = parseSgf(sgf);
		this._go = new Go(this._sgf.info().HA);
		setInfo(this);
		this.first();
	};

	function displayNumber(id, isDisplay) {
		var cafeGo = CafeGo.board[id];
		if (isDisplay) {
			var allTurns = cafeGo._go.turns;
			for (var index = 0; index < allTurns.length; index++) {
				var turns = allTurns[index];
				for (var i in turns) {
					var value = turns[i];
					if (value.set) {
						displayStoneTurnNumber(id, value.point, index + 1);
					}
				}
			}
		} else {
			eraseAllStoneTurnNumber(id);
		}
		var lastTurn = cafeGo._go.lastTurn();
		displayNowStone(id);
	}

	function playAuto(id) {
		var cafeGo = CafeGo.board[id];
		if (false == cafeGo._play.isPlay) {
			return;
		}
		if (false == cafeGo._sgf.next()) {
			cafeGo._play.isPlay = false;
			updatePlayButton(id);
			return;
		}
		var node = parseTurn(cafeGo._sgf.get());
		if (node.error) {
			return;
		}
		var turn = cafeGo._go.set(node.player, node.point);
		if (turn.error) {
			cafeGo._play.isPlay = false;
			updatePlayButton(id);
			return;
		}
		// update display
		updateDisplay(cafeGo._id);
		// do animate
		cafeGo._play.animate = turn.values;
		animateTurn(cafeGo._id);
		return;
	}

	function playPause(id) {
		var cafeGo = CafeGo.board[id];
		if (cafeGo._play.isPlay) {
			cafeGo._play.isPlay = false;
			updatePlayButton(id);
		}
		if (0 < cafeGo._play.animate.length) {
			var turn = cafeGo._play.animate;
			cafeGo._play.animate = [];
			updateTurnOne(id, turn);
		}
	}

	function toggleInfoWindow(event) {
		var cafeGo = CafeGo.board[event.data.id];
		if (event.data.open) {
			cafeGo._infoWindow.css({top: - cafeGo._infoWindow.outerHeight()});
			cafeGo._infoWindow.show();
			cafeGo._infoWindow.animate({top: "0px"}, 500);
		} else {
			cafeGo._infoWindow.animate({top: - cafeGo._infoWindow.outerHeight() + "px"}, 500);
		}
	}

	function toggleControlWindow(event) {
		var cafeGo = CafeGo.board[event.data.id];
		if (event.data.open) {
			cafeGo._controlWindow.animate(
				{top: cafeGo._view.board - cafeGo._controlWindow.outerHeight()}, 500);
		} else {
			cafeGo._controlWindow.animate({top: cafeGo._view.board}, 500);
		}
	}

	function control(event) {
		if (this._lockControl) {return;}
		this._lockControl = true;
		var cafeGo = CafeGo.board[event.data.id];
		switch (event.data.control) {
			case "next" : cafeGo.next();break;
			case "prev" : cafeGo.previous();break;
			case "first": cafeGo.first();break;
			case "last" : cafeGo.last();break;
			case "play" : cafeGo.play();break;
			case "number" : cafeGo.numbering();break;
			default 	: break;
		}
		this._lockControl = false;
	}

	function updateDisplay(id) {
		var cafeGo = CafeGo.board[id];
		$("#" + id + "-control-turn").html(numberString(cafeGo._go.turnCount, 3));
		$("#" + id + "-control-turn-count").html(cafeGo._sgf.count);
		$("#" + id + "-hama-black").html(numberString(cafeGo._go.hamaCount[Go.BLACK],2 ));
		$("#" + id + "-hama-white").html(numberString(cafeGo._go.hamaCount[Go.WHITE], 2));
	}

	function updatePlayButton(id) {
		var cafeGo = CafeGo.board[id];
		var button = $("#" + cafeGo._id + "-control-play")
		button.removeClass("control-" + (cafeGo._play.isPlay ? "play" : "pause"));
		button.addClass("control-" + (!cafeGo._play.isPlay ? "play" : "pause"));
	}

	function updateNumberButton(id) {
		var cafeGo = CafeGo.board[id];
		var button = $("#" + cafeGo._id + "-control-number")
		button.removeClass("control-" + (cafeGo._view.numbering ? "nonum" : "number"));
		button.addClass("control-" + (!cafeGo._view.numbering ? "nonum" : "number"));
	}

	function animateTurn(id) {
		var cafeGo = CafeGo.board[id];
		if (0 == cafeGo._play.animate.length) {
			if (null != cafeGo._play.timerID) {
				clearTimeout(cafeGo._play.timerID);
				cafeGo._play.timerID = null;
			}
			if (cafeGo._play.isPlay) {
				cafeGo._play.timerID = setTimeout(
					function() {playAuto(id)}, cafeGo._play.interval);
			}
			return;
		}
		var turn = cafeGo._play.animate.shift();
		if (turn.set) {
			var stone = createStone(id, turn);
			var boardPoint = turn.point.multiply(cafeGo._view.stone)
				.add(cafeGo._view.padding, cafeGo._view.padding);
			if (Go.BLACK == turn.player) {
				var startTop = cafeGo._view.board;
			} else {
				var startTop = - cafeGo._view.stone;
			}
			stone.css({left: boardPoint.x + "px", top: startTop});
			cafeGo._element.append(stone);
			stone.animate({top: boardPoint.y + "px"}, 500,
				function() {
					displayNowStone(id);
					animateTurn(id)});
		} else {
			var stone = getStone(id, turn.point);
			if (Go.BLACK == turn.player) {
				var startLeft = - cafeGo._view.stone;
			} else {
				var startLeft = cafeGo._view.board;
			}
			stone.animate({left: startLeft}, 500,
				function(){
					removeStone(id, turn.point);
					animateTurn(id);
				});
		}
	}

	function updateTurn(id, turns) {
		var cafeGo = CafeGo.board[id];
		for (var i in turns) {
			var turn = turns[i];
			if (turn.set) {
				var stone = createStone(id, turn);
				var boardPoint = turn.point.multiply(cafeGo._view.stone)
					.add(cafeGo._view.padding, cafeGo._view.padding);
				stone.css({
					left: boardPoint.x + "px",
					top: boardPoint.y + "px"});
				cafeGo._element.append(stone);
			} else {
				removeStone(id, turn.point);
			}
		}
	}

	function updateTurnOne(id, turns) {
		updateTurn(id, turns);
		displayNowStone(id);
	}

	function displayNowStone(id) {
		var cafeGo = CafeGo.board[id];
		var lastTurn = cafeGo._go.lastTurn();
		if (lastTurn.set) {
			$("#" + id + "-stone-now").remove();
			getStone(id, lastTurn.point).append(divJ("now", id + "-stone-now"));
		}
	}

/**
	function redrawBoard(id) {
		removeAllStone(id);
		var cafeGo = CafeGo.board[id];
		updateTurn(id, cafeGo._go.getAll().values);
	}
**/

	var PLAYER_IMAGE = ["", "black", "white"];

	function parseTurn(node) {
		if (node.B) {
			return {player: Go.BLACK, point: toPoint(node.B)};
		} else if (node.W) {
			return {player: Go.WHITE, point: toPoint(node.W)};
		} else {
			return {error: "turn is invalid."};
		}
	}

	function createGameInfo(cafeGo) {
		var infoWindow = divJ("info");
		cafeGo._element.append(infoWindow);
		cafeGo._infoWindow = infoWindow;
		setInfo(cafeGo);
		infoWindow.hide();
		infoWindow.bind("click", {id: cafeGo._id, open: false}, toggleInfoWindow);

		var upHover = divJ("hover");
		cafeGo._element.append(upHover);
		upHover.bind("mouseenter", {id: cafeGo._id, open: true}, toggleInfoWindow);

		return infoWindow;
	}

	function setInfo(cafeGo) {
		var infoWindow = cafeGo._infoWindow;
		var info = cafeGo._sgf.info();
		infoWindow.text("");
		if (info.ERR) {
			infoWindow.append("Error: " + info.ERR);
			return;
		}
		if (info.PB) {
			var text = info.PB;
			if (info.BR) {
				text += " " + info.BR;
			}
			infoWindow.append("<span class='black'>" + text + "</span><br/>");
		}
		if (info.PW) {
			var text = info.PW;
			if (info.WR) {
				text += " " + info.WR;
			}
			infoWindow.append("<span class='white'>" + text + "</span><br/>");
		}
		var text = "";
		if (info.RE) {
			text += "Result:" + info.RE + " ";
		}
		if (info.KM) {
			text += "Komi:" + info.KM + " ";
		}
		if (info.HA && 0 != info.HA) {
			text += "Handicap:" + info.HA + " ";
		}
		if (0 < text.length) {
			infoWindow.append(text + "<br/>");
		}
		if (info.EV) {
			var text = "Event:" + info.EV;
			if (info.RO) {
				text += " Round:" + info.RO;
			}
			text += "<br/>";
			infoWindow.append(text);
		}
		var text = "";
		if (info.PC) {
			text += "Place:" + info.PC + " ";
		}
		if (info.DT) {
			text += "Date:" + info.DT + " ";
		}
		if (0 < text.length) {
			infoWindow.append(text + "<br/>");
		}
		if ("" == infoWindow.text()) {
			infoWindow.append("Nothing...");
		}
	}

	function createControlWindow(cafeGo) {
		var controlWindow = divJ("control");
		cafeGo._element.append(controlWindow);
		var controls = ["first", "prev", "play", "next", "last", "number"];
		for (var i in controls) {
			var name = controls[i];
			var button = divJ("control-" + name, cafeGo._id + "-control-" + name);
			button.bind("click", {id: cafeGo._id, control: name}, control);
			controlWindow.append(button);
		}
		controlWindow.append(
			  "<div class='control-text'>"
			+ span(null, cafeGo._id + "-control-turn", 0)
			+ "/"
			+ span(null, cafeGo._id + "-control-turn-count", 0)
			+ "&nbsp;"
			+ span("black", cafeGo._id + "-hama-black", 0)
			+ "&nbsp;"
			+ span("white", cafeGo._id + "-hama-white", 0)
			+ "</div>");
		var clickable = divJ("control-click");
		controlWindow.append(clickable);
//TODO
//		clickable.css({width: controlWindow.outerWidth(), height: controlWindow.outerHeight()});
		clickable.css({width: cafeGo._view.board, height: cafeGo._view.board});
		clickable.bind("click", {id: cafeGo._id, open: false}, toggleControlWindow);
		controlWindow.css({top: cafeGo._view.board});

		var downHover = divJ("hover");
		cafeGo._element.append(downHover);
		downHover.css({top: cafeGo._view.board - 4 + "px"});
		downHover.bind("mouseenter", {id: cafeGo._id, open: true}, toggleControlWindow);

		return controlWindow;
	}

	function createStone(id, turn){
		var cafeGo = CafeGo.get(id);
		var number = (cafeGo._view.numbering && 0 < cafeGo._go.turnCount) ? cafeGo._go.turnCount : "";
		return divJ(PLAYER_IMAGE[turn.player], id + "-stone-" + toPointStr(turn.point), number);
	}

	function displayStoneTurnNumber(id, point, number) {
		getStone(id, point).text(number);
	}

	function divJ(className, id, text) {
		return jQuery(div(className, id, text));
	}

	function div(className, id, text) {
		return element("div", className, id, text);
	}
	function span(className, id, text) {
		return element("span", className, id, text);
	}

	function element(tag, className, id, text) {
		var e = "<" + tag;
		if (id) {
			e += " id='" + id + "'";
		}
		if (className) {
			e += " class='" + className + "'";
		}
		if (text) {
			e += ">" + text + "</" + tag + ">";
		} else {
			e += " />";
		}
		return e;
	}
/*
	function eraseAllStoneTurnNumber(id) {
		CafeGo.get(id).element.find("div.black,div.white").text("");
	}

	function getStone(id, point){
		return CafeGo.get(id).element.find("#" + id + "-stone-" + toPointStr(point));
	}

	function removeStone(id, point){
		CafeGo.get(id).element.find("#" + id + "-stone-" + toPointStr(point)).remove();
	}

	function removeAllStone(id){
		CafeGo.get(id).element.find("div.black,div.white").remove();
	}
*/
	function eraseAllStoneTurnNumber(id) {
		$("#" + id).find("div.black,div.white").text("");
	}

	function getStone(id, point){
		return $("#" + id + "-stone-" + toPointStr(point));
	}

	function removeStone(id, point){
		$("#" + id + "-stone-" + toPointStr(point)).remove();
	}

	function removeAllStone(id){
		$("#" + id).find("div.black,div.white").remove();
	}

	function toPoint(val) {
		return new Point(toInt(val.charAt(0)), toInt(val.charAt(1)));
	}
	function toPointStr(point) {
		return toString(point.x)+toString(point.y);
	}

	var STRING_POSITION = "abcdefghijklmnopqrstuvwxyz";

	function toInt(v) {
		return STRING_POSITION.indexOf(v);
	}
	function toString(i) {
		return STRING_POSITION.charAt(i);
	}

	function numberString(value, length) {
		var str = "" + value;
		if (str.length < length) {
			var count = length - str.length;
			for (var i = 0; i < count; i++) {
				str = "&nbsp;" + str;
			}
		}
		return str;
	}

	window.CafeGo = CafeGo;
	$(document).ready(function(){
		$(".cafego").each(function(){new CafeGo($(this).attr("id"));});
	});
})();

